package com.tbit.uqbike.protocol.AnalyzeImpl;

import com.tbit.uqbike.protocol.ABaseHandleObj;
import com.tbit.uqbike.protocol.ATerPkg;
import com.tbit.uqbike.tergateway.data.TerGatewayData;
import com.tbit.uqbike.tergateway.entity.AConnInfo;
import com.tbit.uqbike.tergateway.entity.RemoteControl;
import com.tbit.uqbike.tergateway.jt808.CommPkg;
import com.tbit.uqbike.tergateway.jt808.DataUpPkg;
import com.tbit.uqbike.tergateway.jt808.entity.HexItem;
import com.tbit.uqbike.util.ByteUtil;
import com.tbit.uqbike.util.DateUtil;
import com.tbit.utils.BitUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

/**
 * Created by MyWin on 2018/5/16 0016.
 */
public class Jt808DeEnCoder extends AProtocol {
    private static Logger logger = LoggerFactory.getLogger(Jt808DeEnCoder.class);
    public static final String protocolName = "Jt808";

    public static final byte PKG_FLAG = 0x7E;
    public static final byte ESCAPE_CODE = 0x7D;
    public static final byte ESCAPE_SUB_CODE_7E = 0x02;
    public static final byte ESCAPE_SUB_CODE_7D = 0x01;
    // 最小数据包
    public static final Integer LEN_MIN_PKG = 1 + 2 + 2 + 6 + 2 + 1 + 1;
    // MSGID
    public static final int DATA_UP = 0x0001;

    static {

    }

    @Override
    public List<ATerPkg> analyzeTerPkg(AConnInfo conn, ByteBuf in) {
        List<ATerPkg> list = new LinkedList<>();
        int endInv = -1;
        boolean startFlag = false;
        while (in.readableBytes() >= LEN_MIN_PKG) {
            // 找头
            if (!startFlag) {
                while (in.readableBytes() > 0) {
                    if (in.getByte(0) == PKG_FLAG) {
                        startFlag = true;
                        break;
                    } else {
                        in.skipBytes(1);
                    }
                }
            }
            // 找不到头，丢弃所有数据
            if (!startFlag) {
                in.skipBytes(in.readableBytes());
                endInv = -1;
            } else {
                // 找尾
                endInv = -1;
                for (int i = 1; i < in.readableBytes(); i++) {
                    if (in.getByte(i) == PKG_FLAG) {
                        endInv = i;
                        break;
                    }
                }
                if (endInv == -1) {
                    // 数据量大，重新找头，数据量不大继续等待后续数据
                    if (in.readableBytes() > 2048) {
                        startFlag = false;
                        in.skipBytes(1);
                        continue;
                    } else {
                        break;
                    }
                } else {
                    // 取报文
                    if (endInv + 1 > LEN_MIN_PKG) {
                        byte[] data = new byte[endInv + 1];
                        in.getBytes(0, data);
                        // 转义还原
                        int len = deEscape(data);
                        // 报文校验
                        if (Objects.equals(calCheckByte(data, len), data[len - 1 - 1])) {
                            // 数据解析
                            ATerPkg pkg = analyzePkg(conn, data, len);
                            if (null != pkg) {
                                pkg.setConnId(conn.connId);
                                pkg.setProName(getProtocolName());
                                if (pkg.autoRsp()) {
                                    ByteBuf rsp = pkg.getRsp(conn, AAutoProtocol.getProtocol(protocolName));
                                    if (null != rsp && rsp.readableBytes() > 0) {
                                        conn.downMsg(rsp);
                                    }
                                }
                                list.add(pkg);
                            }
                        }
                    }
                    in.skipBytes(endInv + 1);
                    endInv = -1;
                }
            }
        }
        return list;
    }

    // 转义
    public static byte[] escape(byte[] bs) {
        int escCnt = 0;
        for (int i = 1; i < bs.length - 1; i++) {
            if (Objects.equals(bs[i], PKG_FLAG)
                    || Objects.equals(bs[i], ESCAPE_CODE)) {
                escCnt++;
            }
        }
        if (escCnt > 0) {
            byte[] v = new byte[escCnt + bs.length];
            v[0] = bs[0];
            v[v.length - 1] = bs[bs.length - 1];
            int j = 1;
            for (int i = 1; i < bs.length - 1; i++) {
                if (Objects.equals(bs[i], PKG_FLAG)) {
                    v[j++] = ESCAPE_CODE;
                    v[j++] = ESCAPE_SUB_CODE_7E;
                } else if (Objects.equals(bs[i], ESCAPE_CODE)) {
                    v[j++] = ESCAPE_CODE;
                    v[j++] = ESCAPE_SUB_CODE_7D;
                } else {
                    v[j++] = bs[i];
                }
            }
            return v;
        } else {
            return bs;
        }
    }

    // 逆转义
    public static int deEscape(byte[] bs) {
        int escCnt = 0;
        for (int i = 1, j = 1; i < bs.length - 1; i++) {
            if (Objects.equals(bs[i], ESCAPE_CODE) && Objects.equals(bs[i + 1], ESCAPE_SUB_CODE_7E)) {
                bs[j++] = PKG_FLAG;
                i++;
                escCnt++;
            } else if (Objects.equals(bs[i], ESCAPE_CODE) && Objects.equals(bs[i + 1], ESCAPE_SUB_CODE_7D)) {
                bs[j++] = ESCAPE_CODE;
                i++;
                escCnt++;
            } else {
                bs[j++] = bs[i];
            }
        }
        return bs.length - escCnt;
    }

    // 计算校验
    public static byte calCheckByte(byte[] bs, int len) {
        int i = 1;
        byte b = bs[i++];
        for (; i < len - 2; i++) {
            b ^= bs[i];
        }
        return b;
    }

    public static byte calCheckByte(ByteBuf bs, int len) {
        int i = 1;
        byte b = bs.getByte(i++);
        for (; i < len; i++) {
            b ^= bs.getByte(i);
        }
        return b;
    }

    private Date getPkgDt(String dt) {
        if (dt == null || dt.isEmpty()) {
            return new Date();
        } else {
            try {
                return DateUtil.ymdHmsDU.parse(dt);
            } catch (Exception e) {
                return new Date();
            }
        }
    }

    private ATerPkg analyzePkg(AConnInfo conn, byte[] bs, int len) {
        ATerPkg pkg = null;

        int msgId = ByteUtil.getUnsignedShort(bs, 1);
        switch (msgId) {
            case DATA_UP:
                pkg = analyzeDataUp(conn, bs, len);
                break;
            default:
                break;
        }
        if (null != pkg) {
            pkg.signPkg = bs;
        }
        return pkg;
    }

    private ATerPkg analyzeDataUp(AConnInfo conn, byte[] bs, int len) {
        DataUpPkg pkg = new DataUpPkg();
        // 去掉标识符
        int index = 1;
        pkg.initHead(bs, index, len);
        index += pkg.getHeadLen();
        while (len - index >= 4) {
            HexItem hexItem = new HexItem();
            hexItem.init(bs, index);
            index += (hexItem.len + 4);
            pkg.itemList.add(hexItem);
        }
        return pkg;
    }

    public ByteBuf getDataUpRsp(AConnInfo info, DataUpPkg pkg) {
        ByteBuf rsp = getFormatPkgHead(info, pkg);
//region body
//endregion
        rsp = setLenAndCrc(rsp);
        return rsp;
    }

    public ByteBuf getFormatPkgHead(AConnInfo info, CommPkg pkg) {
        ByteBuf rsp = PooledByteBufAllocator.DEFAULT.directBuffer();
        rsp.writeByte(PKG_FLAG); // 头
        rsp.writeShort(pkg.msgId);//id
        rsp.writeShort(CommPkg.getFlag(pkg.isLongPhoneFlag(), false, 0, 0)); // 标志
        // 手机号
        rsp.writeBytes(pkg.phone);
        rsp.writeShort(pkg.serNo);
        return rsp;
    }

    /**
     * 设置长度和校验和
     *
     * @param byteBuf
     * @return
     */
    public static ByteBuf setLenAndCrc(ByteBuf byteBuf) {
        // 设置长度
        int headLen = 1 + 2 + 2 + 2;
        int flag = byteBuf.getUnsignedShort(3);
        if (BitUtil.check(flag, 14)) {
            headLen += 9;
        } else {
            headLen += 6;
        }
        if (BitUtil.check(flag, 13)) {
            headLen += 4;
        }
        flag = CommPkg.reSetLen(flag, byteBuf.readableBytes() - headLen);
        byteBuf.setShort(3, flag);
        // 计算校验和
        byteBuf.writeByte(calCheckByte(byteBuf, byteBuf.readableBytes()));
        // 转义
        byte[] temp = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(temp);
        temp = escape(temp);
        byteBuf.writeBytes(temp);
        // 填入尾部标志
        byteBuf.writeByte(PKG_FLAG);
        return byteBuf;
    }

    @Override
    public ByteBuf builtRemoteControlPkg(AConnInfo conn, ABaseHandleObj aBaseHandleObj) {
        ByteBuf byteBuf = null;
        RemoteControl remoteControl = (RemoteControl) aBaseHandleObj;
        try {
            String serNO = null;
            // 创建完成 写入全局缓存
            if (null != byteBuf) {
                TerGatewayData.setStrSerNoMap(serNO, remoteControl);
                return byteBuf;
            }
        } catch (Exception e) {
            if (byteBuf != null) {
                byteBuf.clear();
            }
            logger.error("构造下行数据包异常", e);
        }
        return null;
    }

    @Override
    public String getProtocolName() {
        return protocolName;
    }
}
